package com.yj.ccs.abs.oa.service;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.yj.ccs.abs.common.enums.AttachmentType;
import com.yj.ccs.abs.common.enums.ContractTransportType;
import com.yj.ccs.abs.common.enums.ContractType;
import com.yj.ccs.abs.common.model.OaResponse;
import com.yj.ccs.abs.oa.entity.OaAsset;
import com.yj.ccs.abs.oa.entity.OaContract;
import com.yj.ccs.abs.oa.entity.OaFile;
import com.yj.ccs.abs.oa.entity.OaPropertyOwnership;
import com.yj.ccs.abs.util.common.IdGenerator;
import com.yj.ccs.abs.util.gson.LocalDateAdapter;
import com.yj.ccs.abs.util.gson.LocalDateTimeAdapter;
import com.yj.ccs.abs.web.asset.entity.Asset;
import com.yj.ccs.abs.web.asset.entity.Contract;
import com.yj.ccs.abs.web.asset.entity.PropertyOwnership;
import com.yj.ccs.abs.web.asset.mapper.AssetMapper;
import com.yj.ccs.abs.web.asset.mapper.ContractMapper;
import com.yj.ccs.abs.web.asset.mapper.PropertyOwnershipMapper;
import com.yj.ccs.abs.web.common.entity.Attachment;
import com.yj.ccs.abs.web.common.mapper.AttachmentMapper;
import com.yj.ccs.abs.web.management.entity.Client;
import com.yj.ccs.abs.web.management.mapper.ClientMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * Copyright 2018 (C) Yunjian-VC
 * Created on : 2018/3/19
 * Author     : Unbelievable9
 **/
@Service
public class IntegrationService {

    private Gson gson;

    private Api api;

    @Value("${retrofit.oa-base-url}")
    private String url;

    private final ClientMapper clientMapper;

    private final AssetMapper assetMapper;

    private final ContractMapper contractMapper;

    private final PropertyOwnershipMapper propertyOwnershipMapper;

    private final AttachmentMapper attachmentMapper;

    @Autowired
    public IntegrationService(ClientMapper clientMapper,
                              AssetMapper assetMapper,
                              ContractMapper contractMapper,
                              PropertyOwnershipMapper propertyOwnershipMapper,
                              AttachmentMapper attachmentMapper) {
        this.clientMapper = clientMapper;
        this.assetMapper = assetMapper;
        this.contractMapper = contractMapper;
        this.propertyOwnershipMapper = propertyOwnershipMapper;
        this.attachmentMapper = attachmentMapper;
    }

    @PostConstruct
    public void init() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        this.api = retrofit.create(Api.class);

        gson = new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapter(LocalDate.class, new LocalDateAdapter())
                .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
                .create();
    }

    private Contract setupContract(Long assetId, OaContract oaContract, ContractType contractType) {
        Contract contract = new Contract();

        BeanUtils.copyProperties(oaContract, contract);
        contract.setAsset_id(assetId);
        contract.setTransport_type(ContractTransportType.fromValue(oaContract.getTransport_type()));
        contract.setType(contractType);

        return contract;
    }

    private List<Attachment> setupContractAttachment(Long contractId, OaContract oaContract) {
        List<Attachment> attachmentList = new ArrayList<>();

        List<Attachment> fileList = generateAttachment(contractId, oaContract.getFile(), AttachmentType.CONTRACT_FILE);
        List<Attachment> consignedList = generateAttachment(contractId, oaContract.getConsigned_file(), AttachmentType.CONTRACT_CONSIGNED);
        List<Attachment> deliveredList = generateAttachment(contractId, oaContract.getDelivered_file(), AttachmentType.CONTRACT_DELIVERED);
        List<Attachment> reckonedList = generateAttachment(contractId, oaContract.getReckoned_file(), AttachmentType.CONTRACT_RECKONED);

        attachmentList.addAll(fileList);
        attachmentList.addAll(consignedList);
        attachmentList.addAll(deliveredList);
        attachmentList.addAll(reckonedList);

        return attachmentList;
    }

    private List<Attachment> generateAttachment(Long referenceId, List<OaFile> oaFileList, AttachmentType attachmentType) {
        List<Attachment> attachmentList = new ArrayList<>();

        if (oaFileList != null) {
            for (OaFile oaFile : oaFileList) {
                Attachment attachment = new Attachment();
                attachment.setReference_id(referenceId);
                attachment.setUrl(url.substring(0, url.length() - 1) + oaFile.getUrl());
                attachment.setName(oaFile.getName());
                attachment.setType(oaFile.getType());
                attachment.setCategory(attachmentType);

                attachmentList.add(attachment);
            }
        }

        return attachmentList;
    }

    /**
     * 解析OA返回Base64编码字符串
     *
     * @param oaResponseString Base64编码字符串
     * @param typeArgument     返回类型
     * @param <T>              泛型
     * @return 解析后的OaResponse对象
     * @throws UnsupportedEncodingException
     */
    public <T> OaResponse<T> oaResponseDecode(String oaResponseString, Type typeArgument) throws UnsupportedEncodingException {
        Type oaResponseType = TypeToken.getParameterized(OaResponse.class, typeArgument).getType();
        String oaResponseJson = new String(Base64Utils.decode(oaResponseString.getBytes()), "UTF-8");

        return gson.fromJson(oaResponseJson, oaResponseType);
    }

    /**
     * 添加企业客户之后通知OA系统
     *
     * @param clientName 企业客户公司名称
     * @return Base64返回结果
     * @throws IOException
     */
    public String addClient(String clientName) throws IOException {
        String json = gson.toJson(clientName);

        Call<String> call = api.addClient(Base64Utils.encodeToString(json.getBytes("UTF-8")));

        return call.execute().body();
    }

    /**
     * 根据OA推送过来的资产信息，创建数据
     *
     * @param oaAsset OA推送过来的资产数据，包含合同和货权信息
     * @return 是否成功
     */
    @Transactional
    public Boolean createAsset(OaAsset oaAsset) {
        OaContract oaUpstreamContract = oaAsset.getUpstream_contract();
        OaContract oaDownstreamContract = oaAsset.getDownstream_contract();
        OaPropertyOwnership oaPropertyOwnership = oaAsset.getProperty_ownership();

        // 获取对应债务人
        Client client = clientMapper.getByName(oaDownstreamContract.getBuyer());

        if (client == null) {
            return false;
        }

        // 资产信息
        Asset asset = new Asset();
        asset.setNumber(IdGenerator.generateAssetId());
        asset.setMoney_sum(oaPropertyOwnership.getAmount().multiply(oaPropertyOwnership.getUnit_price()));
        asset.setStart_date(oaPropertyOwnership.getTransfer_date());
        asset.setPredict_date(asset.getStart_date().plusDays(client.getCredit_day()));

        assetMapper.create(asset);

        // 上游合同
        Contract upstreamContract = setupContract(asset.getId(), oaUpstreamContract, ContractType.UPSTREAM);

        contractMapper.create(upstreamContract);

        List<Attachment> upstreamAttachmentList = setupContractAttachment(upstreamContract.getId(), oaUpstreamContract);

        if (upstreamAttachmentList.size() > 0) {
            attachmentMapper.batchCreate(upstreamAttachmentList);
        }

        // 下游合同
        Contract downstreamContract = setupContract(asset.getId(), oaDownstreamContract, ContractType.DOWNSTREAM);

        contractMapper.create(downstreamContract);

        List<Attachment> downstreamAttachmentList = setupContractAttachment(downstreamContract.getId(), oaDownstreamContract);

        if (downstreamAttachmentList.size() > 0) {
            attachmentMapper.batchCreate(downstreamAttachmentList);
        }

        // 货权信息
        PropertyOwnership propertyOwnership = new PropertyOwnership();
        BeanUtils.copyProperties(oaPropertyOwnership, propertyOwnership);
        propertyOwnership.setAsset_id(asset.getId());

        propertyOwnershipMapper.create(propertyOwnership);

        List<Attachment> poAttachmentList = new ArrayList<>();

        List<Attachment> poPropertyList = generateAttachment(propertyOwnership.getId(), oaPropertyOwnership.getProperty_pic(), AttachmentType.PO_PROPERTY);
        List<Attachment> poToolList = generateAttachment(propertyOwnership.getId(), oaPropertyOwnership.getTool_pic(), AttachmentType.PO_TOOL);
        List<Attachment> poTransportList = generateAttachment(propertyOwnership.getId(), oaPropertyOwnership.getTransport_pic(), AttachmentType.PO_TRANSPORT);
        List<Attachment> poSheetList = generateAttachment(propertyOwnership.getId(), oaPropertyOwnership.getSheet_pic(), AttachmentType.PO_SHEET);
        List<Attachment> poLaboratoryList = generateAttachment(propertyOwnership.getId(), oaPropertyOwnership.getLaboratory_pic(), AttachmentType.PO_LABORATORY);
        List<Attachment> poCertList = generateAttachment(propertyOwnership.getId(), oaPropertyOwnership.getCert_pic(), AttachmentType.PO_CERT);

        poAttachmentList.addAll(poPropertyList);
        poAttachmentList.addAll(poToolList);
        poAttachmentList.addAll(poTransportList);
        poAttachmentList.addAll(poSheetList);
        poAttachmentList.addAll(poLaboratoryList);
        poAttachmentList.addAll(poCertList);

        if (poAttachmentList.size() > 0) {
            attachmentMapper.batchCreate(poAttachmentList);
        }

        return true;
    }
}
